package system.recording

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Context.MEDIA_PROJECTION_SERVICE
import android.content.Intent
import android.content.ServiceConnection
import android.media.projection.MediaProjectionManager
import android.net.Uri
import android.os.Build
import android.os.IBinder
import android.os.PowerManager
import android.provider.Settings
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.PluginRegistry


/** SystemRecordingPlugin */
class SystemRecordingPlugin : FlutterPlugin, MethodCallHandler,
    PluginRegistry.ActivityResultListener, PluginRegistry.RequestPermissionsResultListener,
    ActivityAware {
    private lateinit var channel: MethodChannel

    private lateinit var context: Context

    private val screenCaptureIRequestCode = 666
    private val isIgnoringBatteryOptimizationsCode = 888
    private val audioPermissionRequestCode = 999
    private var result: MethodChannel.Result? = null
    override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        context = flutterPluginBinding.applicationContext
        channel = MethodChannel(flutterPluginBinding.binaryMessenger, "system.recording")
        channel.setMethodCallHandler(this)
    }


    private var source = 0

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        when (call.method) {
            "initialize" -> {
                this.result = result
                if (audioRecordService != null) {
                    result.success(false)
                    return
                }
                source = call.argument<Int>("source") ?: 0
                initialize()
            }

            "requestIgnoreBatteryOptimizations" -> {
                if (!isIgnoringBatteryOptimizations()) {
                    this.result = result
                    requestIgnoreBatteryOptimizations()
                    return
                }
                result.success(true)
            }

            "startRecording" -> {
                val value = audioRecordService?.startRecording()
                result.success(value ?: false)
            }

            "stopRecording" -> {
                val value = audioRecordService?.stopRecording()
                result.success(value ?: false)
            }

            "dispose" -> {
                try {
                    context.stopService(AudioRecordService.getIntent(context))
                    activityBinding.activity.unbindService(serviceConnection)
                    result.success(true)
                    audioRecordService = null
                } catch (e: Exception) {
                    result.success(false)
                }
            }

            else -> result.success(null)
        }
    }

    private fun initialize() {
        val permissions = mutableListOf(
            Manifest.permission.RECORD_AUDIO
        )
        if (source == 0) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
                permissions.add(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION)
            }
            ActivityCompat.requestPermissions(
                activityBinding.activity, permissions.toTypedArray(), audioPermissionRequestCode
            )
        } else if (source == 1) {
            permissions.add(Manifest.permission.CAPTURE_AUDIO_OUTPUT)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
                permissions.add(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION)
            }
            ActivityCompat.requestPermissions(
                activityBinding.activity, permissions.toTypedArray(), audioPermissionRequestCode
            )
        }
    }

    private fun isIgnoringBatteryOptimizations(): Boolean {
        val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
        return powerManager.isIgnoringBatteryOptimizations(context.packageName)
    }

    @SuppressLint("BatteryLife")
    private fun requestIgnoreBatteryOptimizations() {
        try {
            val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
            intent.setData(Uri.parse("package:" + context.packageName))
            activityBinding.activity.startActivityForResult(
                intent, isIgnoringBatteryOptimizationsCode
            )
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
    }

    private var audioRecordService: AudioRecordService? = null

    private val serviceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            result?.success(true)
            result = null
            val binder = service as AudioRecordService.AudioRecordServiceBinder
            audioRecordService = binder.getService()
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            audioRecordService = null
        }
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel.setMethodCallHandler(null)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == screenCaptureIRequestCode) {
                val intent = AudioRecordService.getIntent(context)
                intent.putExtra("resultData", data)
                startForegroundService(intent)
            }
        }
        if (requestCode == isIgnoringBatteryOptimizationsCode) {
            result?.success(isIgnoringBatteryOptimizations())
            result = null
        }
        return false
    }

    private fun startForegroundService(intent: Intent) {
        intent.putExtra("source", source)
        ContextCompat.startForegroundService(context, intent)
        activityBinding.activity.bindService(
            intent, serviceConnection, Context.BIND_AUTO_CREATE
        )
    }

    private lateinit var activityBinding: ActivityPluginBinding
    override fun onAttachedToActivity(binding: ActivityPluginBinding) {
        activityBinding = binding
        binding.addActivityResultListener(this)
        binding.addRequestPermissionsResultListener(this)
    }

    override fun onDetachedFromActivityForConfigChanges() {
        onDetachedFromActivity()
    }

    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
        onAttachedToActivity(binding)
    }

    override fun onDetachedFromActivity() {
        activityBinding.removeActivityResultListener(this)
        activityBinding.removeRequestPermissionsResultListener(this)
    }

    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<out String>, grantResults: IntArray
    ): Boolean {
        if (requestCode == audioPermissionRequestCode && permissions.isNotEmpty()) {
            if (permissions.contains(Manifest.permission.CAPTURE_AUDIO_OUTPUT)) {
                val mProjectionManager =
                    context.getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
                val screenCaptureIntent = mProjectionManager.createScreenCaptureIntent()
                activityBinding.activity.startActivityForResult(
                    screenCaptureIntent, screenCaptureIRequestCode
                )
            } else {
                startForegroundService(AudioRecordService.getIntent(context))
            }
        }
        return false
    }
}
